#pragma once

#include <list>
#include <vector>
#include <deque>

#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>

#include <boost/guid.hpp>

using boost::asio::ip::tcp;

namespace cm {

	typedef std::list<std::string> strings;
	typedef std::list<boost::guid> ids;

namespace client_server {

	// -- Questi tipi vengono passati tra server e client e viceversa --

	// Attenzione: per quanto riguarda le dimensioni dei tipo la piattaforma di riferimento e Win32
	// Attenzione: per quanto riguarda la rappresentazione big-endian e little-endian la piattaforma di riferimento e Win32 (Utilizzare cose simili a htons)

	typedef unsigned long  size_t;
	enum {size_t_size		= sizeof(cm::client_server::size_t)};

	typedef unsigned short str_len;
	enum {str_len_size		= sizeof(str_len)};

	typedef unsigned long session_id; 
	enum {session_id_size	= sizeof(session_id)};

	typedef unsigned long file_len;
	enum {file_len_size = sizeof(file_len)};

	typedef unsigned long version_age;
	enum {version_age_size = sizeof(version_age)};

	//--------------------------------------------

	enum {server_session_id	= 1};

	//--------------------------------------------

	enum {identify_name_max_length	= 100};
	enum {identify_pwd_max_length	= 100};
	enum {identify_email_max_length	= 100};

	//----------------------------------------------------------------------

	enum {room_name_max_length		= 100};

	//----------------------------------------------------------------------

	enum { keep_alive_timeout_server		= 30 };	// in seconds
	enum { keep_alive_max_attempts_server	= 3 };
	enum { keep_alive_timeout_client		= 20 };	// in seconds
	enum { keep_alive_max_attempts_client	= 3 };

	//----------------------------------------------------------------------

	enum { status_header_lenght = cm::client_server::size_t_size + cm::client_server::size_t_size};

	//----------------------------------------------------------------------

	static const char* authority_identity_id = "{30B74694-D2EC-49a5-A6BF-4A17C7517DA6}";

	struct participant_prop
	{
		boost::guid					id_;
		std::string					name_;
	};
	typedef std::list<participant_prop> participant_props;

	//----------------------------------------------------------------------

	struct room_prop
	{
		room_prop() : partipants_counter_(0) {}
		boost::guid					id_;
		std::string					name_;
		cm::client_server::size_t	partipants_counter_;
		participant_props			partipants_props_;
	};
	typedef std::list<room_prop>	rooms_props;

	static const char* default_room = "{B2715FF4-45E3-42c7-9563-64934C0A9317}";

	//----------------------------------------------------------------------

	struct server_status_info
	{
		server_status_info() : user_counter_in_room_(0),user_counter_total_(0) {}
		server_status_info(const cm::client_server::size_t& in_room_users,const cm::client_server::size_t& total_users)
			: user_counter_in_room_(in_room_users),user_counter_total_(total_users)
		{}
		bool operator!=(const server_status_info& other) const
		{
			return (user_counter_in_room_ != other.user_counter_in_room_ || user_counter_total_ != other.user_counter_total_);
		}
		cm::client_server::size_t user_counter_in_room_;
		cm::client_server::size_t user_counter_total_;
	};

	//----------------------------------------------------------------------

	class raw_file
	{
		enum {part_size = 5120};// 5 KByte per parte
	public:
		raw_file(const boost::filesystem::path& dir,const char* file_name) 
			: file_name_(file_name)
		{
			boost::filesystem::ifstream	file;
			//file.exceptions(boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit);
			file.open(dir / file_name_,std::ios::in | std::ios::binary | std::ios::ate);
			if (file.is_open() == false)
			{
				return;
			}
			cm::client_server::file_len size = file.tellg();
			if (size == 0)
			{
				return;
			}
			data_.resize(size);
			file.seekg(0,std::ios::beg);
			file.read((char *)&data_[0],size);
			file.close();
		}
		raw_file(const char* file_name,const client_server::file_len& size) 
			: file_name_(file_name),data_(size)
		{
		}
		raw_file(const char* file_name,const client_server::file_len& size,const char* data) 
			: file_name_(file_name),data_(size)
		{
			memcpy(&data_[0],data,size);
		}
		~raw_file()
		{
		}
	public:
		const std::string& file_name() const {return file_name_;}
		const unsigned char* data() const {return &data_[0];}
		cm::client_server::file_len size() const {return data_.size();}
	public:
		cm::client_server::file_len get_parts() const 
		{
			return data_.size() / part_size + ((data_.size() % part_size) != 0 ? 1 : 0);
		}
		cm::client_server::file_len get_part_size(const cm::client_server::file_len& part) const
		{
			if (part + 1 == get_parts())
			{
				return data_.size() % part_size == 0 ? part_size : data_.size() % part_size;
			}
			return part_size;
		}
		const unsigned char* get_data_part(const cm::client_server::file_len& part) const
		{
			return &data_[part * part_size];
		}
		void add_part(const unsigned char* data,const cm::client_server::file_len& part,const cm::client_server::file_len& size)
		{
			memcpy(&data_[part * part_size],data,size);
		}
	public:
		void write_to_disk(const boost::filesystem::path& dir) const
		{
			boost::filesystem::create_directories(dir);

			boost::filesystem::ofstream	file;
			file.open(dir / file_name_,std::ios::out | std::ios::binary | std::ios::trunc);
			file.write((char *)&data_[0],data_.size());
			file.close();
		}
	private:
		std::string					file_name_;
		std::vector<unsigned char>	data_;
	};

	typedef boost::shared_ptr<raw_file> raw_file_ptr;
}}
